package com.mxl.datebase.orm.hibernate;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.engine.NamedQueryDefinition;
import org.hibernate.engine.NamedSQLQueryDefinition;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.impl.CriteriaImpl;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.transform.AliasToEntityMapResultTransformer;
import org.hibernate.transform.ResultTransformer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.mxl.datebase.orm.BaseDao;
import com.mxl.datebase.orm.pagination.Page;
/**
 * BaseDao的实现类,所有的Dao implement应该extends com.bill99.seashell.orm.hibernate.BaseDaoSupport<T, PK>.
 * 
 * @param <T> 实体类
 * @param <PK> 实体类的主键类型
 * 
 * @author
 */

public class BaseDaoSupport<T, PK extends Serializable> extends HibernateDaoSupport implements BaseDao<T, PK> {

	protected Logger logger = LoggerFactory.getLogger(getClass());

	protected Class<T> entityClass;

	/**
	 * 通过子类的泛型定义取得对象类型Class.
	 * eg.
	 * public class UserDao extends DaoSupport<User, Long>
	 */
	@SuppressWarnings("unchecked")
	public BaseDaoSupport() {
		Type[] params = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments();
		this.entityClass = (Class<T>) params[0];
	}

	public BaseDaoSupport(Class<T> entityClass) {
		this.entityClass = entityClass;
	}
	
	public Type getModelType() {
		return entityClass;
	}

	/* (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#save(T)
	 */
	public T save(final T entity) {
		getHibernateTemplate().saveOrUpdate(entity);
		logger.debug("save entity: {}", entity);
		return entity;
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#update(java.lang.Object)
	 */
	public T update(final T entity) {
		getHibernateTemplate().update(entity);
		return entity;
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#merge(java.lang.Object)
	 */
	public T merge(T entity) {
		getHibernateTemplate().merge(entity);
		return entity;
	}

	/* (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#delete(T)
	 */
	public void delete(final T entity) {
		getHibernateTemplate().delete(entity);
		logger.debug("delete entity: {}", entity);
	}
	
	/* (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#delete(PK)
	 */
	public void delete(final PK id) {
		getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				Object model = session.get(entityClass, id);
				if (model != null) {
					session.delete(model);
					logger.debug("delete entity {},id is {}", entityClass.getSimpleName(), id);
				}
				return null;
			}
		});
	}

	/* (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#get(PK)
	 */
	@SuppressWarnings("unchecked")
	public T get(final PK id) {
		return (T) getHibernateTemplate().get(entityClass, id);
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#getForUpdate(java.io.Serializable)
	 */
	@SuppressWarnings("unchecked")
	public T getForUpdate(final PK id) {
		return (T) getHibernateTemplate().get(entityClass, id, LockMode.PESSIMISTIC_WRITE);
	}

	/* (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#get(java.util.Collection)
	 */
	public List<T> get(final Collection<PK> ids) {
		return find(Restrictions.in(getIdName(), ids));
	}

	/* (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#getAll()
	 */
	public List<T> getAll() {
		return find();
	}
	
	/*(non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#getAll(com.bill99.seashell.orm.pagination.Page)
	 */
	@SuppressWarnings("unchecked")
	public List<T> getAll(final Page page) {
		if (page == null) {
			return find();
		} else {
			DetachedCriteria criteria = createCriteria();
			int total = countCriteria(criteria);
			page.setTotalRecord(total);
			int firstResult = (page.getTargetPage()-1) * page.getPageSize();
			int maxResults = page.getPageSize();
			return getHibernateTemplate().findByCriteria(criteria, firstResult, maxResults);
		}
	}

	/* (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#getAll(java.lang.String, boolean)
	 */
	@SuppressWarnings("unchecked")
	public List<T> getAll(final String orderByProperty, final boolean isAsc) {
		Order order = null;
		if (isAsc) {
			order = Order.asc(orderByProperty);
		} else {
			order = Order.desc(orderByProperty);
		}
		DetachedCriteria criteria = createCriteria(null, new Order[] {order});
		return getHibernateTemplate().findByCriteria(criteria);
	}

	/* (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#findBy(java.lang.String, java.lang.Object)
	 */
	public List<T> findBy(final String propertyName, final Object value) {
		Criterion criterion = Restrictions.eq(propertyName, value);
		return find(criterion);
	}

	/* (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#findUniqueBy(java.lang.String, java.lang.Object)
	 */
	@SuppressWarnings("unchecked")
	public T findUniqueBy(final String propertyName, final Object value) {
		DetachedCriteria criteria = createCriteria(Restrictions.eq(propertyName, value));
		List<T> list = getHibernateTemplate().findByCriteria(criteria, -1, 1);
		return list.isEmpty() ? null : list.get(0);
	}

	/* (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#find(java.lang.String, java.lang.Object)
	 */
	public <X> List<X> find(final String hql, final Object... values) {
		return find(hql, null, values);
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#iterator(java.lang.String, java.lang.Object[])
	 */
	@SuppressWarnings("unchecked")
	public <X> Iterator<X> iterator(String hql, Object... values) {
		return getHibernateTemplate().iterate(hql, values);
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#find(java.lang.String, com.bill99.seashell.orm.pagination.Page, java.lang.Object[])
	 */
	@SuppressWarnings("unchecked")
	public <X> List<X> find(final String hql, final Page page, final Object... values) {
		return getHibernateTemplate().executeFind(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				Query q = createQuery(session, hql, values);
				if (page != null) {
					int totalCount = countHqlResult(hql, values);
					page.setTotalRecord(totalCount);
					q.setFirstResult((page.getTargetPage()-1) * page.getPageSize());
					q.setMaxResults(page.getPageSize());
				}
				return q.list();
			}
		});
	}

	/* (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#find(java.lang.String, java.util.Map)
	 */
	public <X> List<X> find(final String hql, final Map<String, ?> values) {
		return find(hql, null, values);
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#iterator(java.lang.String, java.util.Map)
	 */
	@SuppressWarnings("unchecked")
	public <X> Iterator<X> iterator(final String hql, final Map<String, ?> values) {
		return (Iterator<X>) getHibernateTemplate().executeWithNativeSession(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException {
				Query queryObject = session.createQuery(hql);
				queryObject.setProperties(values);
				return queryObject.iterate();
			}
		});
	}

	/*
	 * (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#find(java.lang.String, com.bill99.seashell.orm.pagination.Page, java.util.Map)
	 */
	@SuppressWarnings("unchecked")
	public <X> List<X> find(final String hql, final Page page, final Map<String, ?> values) {
		return getHibernateTemplate().executeFind(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				Query query = createQuery(session, hql, values);
				if (page != null) {
					int totalCount = countHqlResult(hql, values);
					page.setTotalRecord(totalCount);
					query.setFirstResult((page.getTargetPage()-1) * page.getPageSize());
					query.setMaxResults(page.getPageSize());
				}
				return query.list();
			}
		});
	}

	/* (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#find(org.hibernate.criterion.Criterion)
	 */
	public List<T> find(final Criterion... criterions) {
		return find((Order[])null, criterions);
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#find(org.hibernate.criterion.Order[], org.hibernate.criterion.Criterion[])
	 */
	@SuppressWarnings("unchecked")
	public List<T> find(final Order[] orders, final Criterion... criterions) {
		DetachedCriteria criteria = createCriteria(null, orders, criterions);
		return getHibernateTemplate().findByCriteria(criteria);
	}

	/*
	 * (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#find(com.bill99.seashell.orm.pagination.Page, org.hibernate.criterion.Criterion[])
	 */
	public List<T> find(Page page, Criterion... criterions) {
		return find(null, page, null, criterions);
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#find(java.util.List, org.hibernate.criterion.Criterion[])
	 */
	public <X> List<X> find(List<Projection> projections, Criterion... criterions) {
		return find(projections, null, criterions);
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#find(java.util.List, org.hibernate.criterion.Order[], org.hibernate.criterion.Criterion[])
	 */
	public <X> List<X> find(List<Projection> projections, Order[] orders, Criterion... criterions) {
		return find(projections, null, orders, criterions);
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#find(java.util.List, com.bill99.seashell.orm.pagination.Page, org.hibernate.criterion.Order[], org.hibernate.criterion.Criterion[])
	 */
	@SuppressWarnings("unchecked")
	public <X> List<X> find(final List<Projection> projections, final Page page, final Order[] orders, final Criterion... criterions) {
		DetachedCriteria criteria = createCriteria(projections, orders, criterions);
		if (page == null) {
			return getHibernateTemplate().findByCriteria(criteria);
		} else {
			int total = countCriteria(criteria);
			page.setTotalRecord(total);
			int firstResult = (page.getTargetPage()-1) * page.getPageSize();
			int maxResults = page.getPageSize();
			return getHibernateTemplate().findByCriteria(criteria, firstResult, maxResults);
		}
	}

	/*
	 * (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#find(com.bill99.seashell.orm.pagination.Page, org.hibernate.criterion.Order[], org.hibernate.criterion.Criterion[])
	 */
	public List<T> find(Page page, Order[] orders, Criterion... criterions) {
		return find(null, page, orders, criterions);
	}
	
	/* (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#findUnique(java.lang.String, java.lang.Object)
	 */
	@SuppressWarnings("unchecked")
	public <X> X findUnique(final String hql, final Object... values) {
		return (X)getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				return createQuery(session, hql, values).setMaxResults(1).uniqueResult();
			}
		});
	}

	/* (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#findUnique(java.lang.String, java.util.Map)
	 */
	@SuppressWarnings("unchecked")
	public <X> X findUnique(final String hql, final Map<String, ?> values) {
		return (X)getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				return createQuery(session, hql, values).setMaxResults(1).uniqueResult();
			}
		});
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#sum(java.lang.String, org.hibernate.criterion.Criterion[])
	 */
	@SuppressWarnings("unchecked")
	public <X> X sum(final String propertyName, final Criterion... criterions) {
		DetachedCriteria criteria = createCriteria(criterions);
		criteria.setProjection(Projections.sum(propertyName));
		List<X> list = getHibernateTemplate().findByCriteria(criteria);
		return list.get(0);
	}

	/*
	 * (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#avg(java.lang.String, org.hibernate.criterion.Criterion[])
	 */
	@SuppressWarnings("unchecked")
	public <X> X avg(final String propertyName, final Criterion... criterions) {
		DetachedCriteria criteria = createCriteria(criterions);
		criteria.setProjection(Projections.avg(propertyName));
		List<X> list = getHibernateTemplate().findByCriteria(criteria);
		return list.get(0);
	}

	/*
	 * (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#max(java.lang.String, org.hibernate.criterion.Criterion[])
	 */
	@SuppressWarnings("unchecked")
	public <X> X max(final String propertyName, final Criterion... criterions) {
		DetachedCriteria criteria = createCriteria(criterions);
		criteria.setProjection(Projections.max(propertyName));
		List<X> list = getHibernateTemplate().findByCriteria(criteria);
		return list.get(0);
	}

	/*
	 * (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#min(java.lang.String, org.hibernate.criterion.Criterion[])
	 */
	@SuppressWarnings("unchecked")
	public <X> X min(final String propertyName, final Criterion... criterions) {
		DetachedCriteria criteria = createCriteria(criterions);
		criteria.setProjection(Projections.min(propertyName));
		List<X> list = getHibernateTemplate().findByCriteria(criteria);
		return list.get(0);
	}

	/*
	 * (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#count(org.hibernate.criterion.Criterion[])
	 */
	public long count(final Criterion... criterions) {
		DetachedCriteria criteria = createCriteria(criterions);
		criteria.setProjection(Projections.rowCount());
		@SuppressWarnings("unchecked")
		List<Long> list = getHibernateTemplate().findByCriteria(criteria);
		return list.get(0);
	}

	/* (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#findByNamedQuery(java.lang.String, java.lang.Object)
	 */
	public <X> List<X> findByNamedQuery(final String queryName, final Object... values) {
		return findByNamedQuery(queryName, null, values);
	}

	/* (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#findByNamedQuery(java.lang.String, java.util.Map)
	 */
	public <X> List<X> findByNamedQuery(final String queryName, final Map<String, ?> values) {
		return findByNamedQuery(queryName, null, values);
	}

	/*
	 * (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#findByNamedQuery(java.lang.String, com.bill99.seashell.orm.pagination.Page, java.lang.Object[])
	 */
	@SuppressWarnings("unchecked")
	public <X> List<X> findByNamedQuery(final String queryName, final Page page, final Object... values) {
		return getHibernateTemplate().executeFind(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				Query query = session.getNamedQuery(queryName);
				if (page != null) {
					int totalCount = countNamedQueryResult(queryName, values);
					page.setTotalRecord(totalCount);
					query.setFirstResult((page.getTargetPage()-1) * page.getPageSize());
					query.setMaxResults(page.getPageSize());
				}
				if (values != null) {
					for (int i = 0; i < values.length; i++) {
						query.setParameter(i, values[i]);
					}
				}
				return query.list();
			}
		});
	}

	/*
	 * (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#findByNamedQuery(java.lang.String, com.bill99.seashell.orm.pagination.Page, java.util.Map)
	 */
	@SuppressWarnings("unchecked")
	public <X> List<X> findByNamedQuery(final String queryName, final Page page, final Map<String, ?> values) {
		return getHibernateTemplate().executeFind(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException,
					SQLException {
				Query query = session.getNamedQuery(queryName);
				if (page != null) {
					int totalCount = countNamedQueryResult(queryName, values);
					page.setTotalRecord(totalCount);
					query.setFirstResult((page.getTargetPage()-1) * page.getPageSize());
					query.setMaxResults(page.getPageSize());
				}
				if (values != null) {
					query.setProperties(values);
				}
				return query.list();
			}
		});
	}
	
	/* (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#batchExecute(java.lang.String, java.lang.Object)
	 */
	public int execute(final String hql, final Object... values) {
		return (Integer)getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException,
					SQLException {
				return createQuery(session, hql, values).executeUpdate();
			}
		});
	}

	/* (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#batchExecute(java.lang.String, java.util.Map)
	 */
	public int execute(final String hql, final Map<String, ?> values) {
		return (Integer)getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException,
					SQLException {
				return createQuery(session, hql, values).executeUpdate();
			}
		});
	}

	/**
	 * 在非事务环境下，session不能正常关闭释放连接，禁止使用
	 */
	@Deprecated
	protected Query createQuery(final String queryString, final Object... values) {
		Query query = getSession().createQuery(queryString);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		return query;
	}
	
	private Query createQuery(Session session, final String queryString, final Object... values) {
		Query query = session.createQuery(queryString);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		return query;
	}
	
	private Query createQuery(Session session, final String queryString, final Map<String, ?> values) {
		Query query = session.createQuery(queryString);
		if (values != null) {
			query.setProperties(values);
		}
		return query;
	}

	/* (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#findUnique(org.hibernate.criterion.Criterion)
	 */
	@SuppressWarnings("unchecked")
	public T findUnique(final Criterion... criterions) {
		DetachedCriteria criteria = createCriteria(criterions);
		List<T> list = getHibernateTemplate().findByCriteria(criteria, -1, 1);
		return list.isEmpty() ? null : list.get(0);
	}
	
	protected DetachedCriteria createCriteria(Criterion... criterions) {
		return createCriteria((List<Projection>)null, (Order[])null, criterions);
	}
	
	protected DetachedCriteria createCriteria(List<Projection> projections, Order[] orders, Criterion... criterions) {
		DetachedCriteria criteria = DetachedCriteria.forClass(entityClass);
		if (projections != null) {
			for (Projection projection : projections) {
				criteria.setProjection(projection);
			}
		}
		if (orders != null) {
			for (int i = 0; i < orders.length; i++) {
				if (orders[i] != null) {
					criteria.addOrder(orders[i]);
				}
			}
		}
		for (Criterion criterion : criterions) {
			criteria.add(criterion);
		}
		return criteria;
	}

	/**
	 * 初始化对象.
	 * 使用load()方法得到的仅是对象Proxy, 在传到View层前需要进行初始化.
	 * 如果传入entity, 则只初始化entity的直接属性,但不会初始化延迟加载的关联集合和属性.
	 * 如需初始化关联属性,需执行:
	 * Hibernate.initialize(user.getRoles())，初始化User的直接属性和关联集合.
	 * Hibernate.initialize(user.getDescription())，初始化User的直接属性和延迟加载的Description属性.
	 */
	protected void initProxyObject(Object proxy) {
		Hibernate.initialize(proxy);
	}

	/**
	 * 在事务环境中flush当前Session的数据到数据库.
	 */
	protected void flush() {
		getHibernateTemplate().flush();
	}
	
	/**
	 * 在事务环境中清除当前Session
	 */
	protected void clear() {
		getHibernateTemplate().clear();
	}

	/*
	 * (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#refresh(java.lang.Object)
	 */
	public T refresh(T entity) {
		getHibernateTemplate().refresh(entity);
		return entity;
	}
	
	public void evict(T entity) {
		getHibernateTemplate().evict(entity);
	};
	
	/* (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#getIdName()
	 */
	public String getIdName() {
		ClassMetadata meta = getSessionFactory().getClassMetadata(entityClass);
		return meta.getIdentifierPropertyName();
	}

	/* (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#isPropertyUnique(java.lang.String, java.lang.Object, java.lang.Object)
	 */
	public boolean isPropertyUnique(final String propertyName, final Object newValue, final Object oldValue) {
		if (newValue == null || newValue.equals(oldValue)) {
			return true;
		}
		Criterion criterion = Restrictions.eq(propertyName, newValue);
		List<T> list = find(criterion);
		return list.isEmpty();
	}

	/* (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#execNamedQuery(java.lang.String, java.lang.Object)
	 */
	public int executeNamedQuery(final String queryName, final Object... values) {
		return (Integer)getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				Query query = session.getNamedQuery(queryName);
				if (values != null) {
					for (int i = 0; i < values.length; i++) {
						query.setParameter(i, values[i]);
					}
				}
				return query.executeUpdate();
			}
		});
	}

	/* (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#execSql(java.lang.String)
	 */
	public int executeSqlUpdate(final String nativeSql, final Object... values) {
		return (Integer)getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				Query query = session.createSQLQuery(nativeSql);
				if (values != null) {
					for (int i = 0; i < values.length; i++) {
						query.setParameter(i, values[i]);
					}
				}
				return query.executeUpdate();
			}
		});
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#execSql(java.lang.String, java.lang.Object[])
	 */
	@SuppressWarnings("unchecked")
	public <X> List<X> executeSql(final String nativeSql, final Object... values) {
		return getHibernateTemplate().executeFind(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				Query query = session.createSQLQuery(nativeSql);
				if (values != null) {
					for (int i = 0; i < values.length; i++) {
						query.setParameter(i, values[i]);
					}
				}
				return query.list();
			}
		});
	}

	/*
	 * (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#executeSqlModel(java.lang.String, java.lang.Object[])
	 */
	@SuppressWarnings("unchecked")
	public List<T> executeSqlModel(final String nativeSql, final Object... values) {
		return getHibernateTemplate().executeFind(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				Query query = session.createSQLQuery(nativeSql).addEntity(entityClass);
				if (values != null) {
					for (int i = 0; i < values.length; i++) {
						query.setParameter(i, values[i]);
					}
				}
				return query.list();
			}
		});
	}
	/*
	 * (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#executeSqlMap(java.lang.String, java.lang.Object[])
	 */
	@SuppressWarnings({"rawtypes" })
	public List<Map> executeSqlMap(String nativeSql, Object[] values) {
		return executeSqlMap(null, nativeSql, values);
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#executeSqlMap(com.bill99.seashell.orm.pagination.Page, java.lang.String, java.lang.Object[])
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public List<Map> executeSqlMap(final Page page, final String nativeSql, final Object[] values) {
		return getHibernateTemplate().executeFind(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				Query query = session.createSQLQuery(nativeSql);
				query.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
				if (values != null) {
					for (int i = 0; i < values.length; i++) {
						query.setParameter(i, values[i]);
					}
				}
				if (page != null) {
					query.setFirstResult((page.getTargetPage()-1) * page.getPageSize());
					query.setMaxResults(page.getPageSize());
					
					StringBuffer countSql = new StringBuffer("select count(*) from  (").append(nativeSql).append(")");
					Query countQuery = session.createSQLQuery(countSql.toString());
					if (values != null) {
						for (int i = 0; i < values.length; i++) {
							countQuery.setParameter(i, values[i]);
						}
					}
					countQuery.setMaxResults(1);
					String tatalCount = countQuery.uniqueResult().toString();
					page.setTotalRecord(new Integer(tatalCount));
				}
				return query.list();
			}
		});
	}
	
	/* (non-Javadoc)
	 * @see com.bill99.seashell.orm.BaseDao#getSeqNextValue(java.lang.String)
	 */
	public BigDecimal getSeqNextValue(final String sequenceName) {
		return (BigDecimal) getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				String sql = "SELECT " + sequenceName + ".NEXTVAL FROM Dual";
				Object nextValue = session.createSQLQuery(sql).uniqueResult();
				return nextValue;
			}
		});
	}
	
	/**
	 * 执行count查询获得本次Criteria查询所能获得的对象总数.
	 */
	private int countCriteria(final DetachedCriteria criteria) {
		return (Integer)getHibernateTemplate().executeWithNativeSession(new HibernateCallback() {
			@SuppressWarnings({ "rawtypes", "unchecked" })
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				Criteria executableCriteria = criteria.getExecutableCriteria(session);
				CriteriaImpl impl = (CriteriaImpl) executableCriteria;

				// 先把Projection、ResultTransformer、OrderBy取出来,清空三者后再执行Count操作
				Projection projection = impl.getProjection();
				ResultTransformer transformer = impl.getResultTransformer();

				List<CriteriaImpl.OrderEntry> orderEntries = null;
				try {
					orderEntries = (List) getFieldValue(impl, "orderEntries");
					setFieldValue(impl, "orderEntries", new ArrayList());
				} catch (Exception e) {
					logger.error("impossible exception:{}", e.getMessage());
				}

				// 执行Count查询
				Long totalCountObject = new Long(executableCriteria.setProjection(Projections.rowCount()).uniqueResult().toString());
				int totalCount = (totalCountObject != null) ? totalCountObject.intValue() : 0;

				// 将之前的Projection,ResultTransformer和OrderBy条件重新设回去
				executableCriteria.setProjection(projection);

				if (projection == null) {
					executableCriteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
				}
				if (transformer != null) {
					executableCriteria.setResultTransformer(transformer);
				}
				try {
					setFieldValue(impl, "orderEntries", orderEntries);
				} catch (Exception e) {
					logger.error("impossible exception:{}", e.getMessage());
				}
				return totalCount;
			}
		});
	}

	/**
	 * 执行count查询获得本次Sql查询所能获得的对象总数.
	 * 
	 * 本函数只能自动处理简单的sql语句,复杂的sql查询请另行编写count语句查询.
	 */
	private int countNamedQueryResult(final String queryName, final Object...values) {
		Query query = createCountNamedQuery(queryName);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		Long count = (Long) query.uniqueResult();
		return (count != null) ? count.intValue() : 0;
	}

	/**
	 * 执行count查询获得本次Sql查询所能获得的对象总数.
	 * 
	 * 本函数只能自动处理简单的sql语句,复杂的sql查询请另行编写count语句查询.
	 */
	private int countNamedQueryResult(final String queryName, final Map<String, ?> values ) {
		Query query = createCountNamedQuery(queryName);
		if (values != null) {
			query.setProperties(values);
		}
		Long count = (Long) query.uniqueResult();
		return (count != null) ? count.intValue() : 0;
	}
	
	/**
	 * 创建执行count查询Query对象.
	 * 
	 * 本函数只能自动处理简单的sql语句,复杂的sql查询请另行编写count语句查询.
	 */
	private Query createCountNamedQuery(final String queryName) {
		NamedQueryDefinition nqd = ((SessionFactoryImplementor)getSessionFactory()).getNamedQuery(queryName);
		Query query = null;
		if (nqd != null) {//namedQuery来自于.hbm.xml映射文件中的<query>
			String countSql = prepareCountHql(nqd.getQueryString());
			query = getSession().createQuery(countSql);
		} else {//namedQuery来自于.hbm.xml映射文件中的<sql-query>
			NamedSQLQueryDefinition nsqd = ((SessionFactoryImplementor)getSessionFactory()).getNamedSQLQuery(queryName);
			String countSql = prepareCountHql(nsqd.getQueryString());
			query = getSession().createSQLQuery(countSql);
		}
		return query;
	}
	
	/**
	 * 执行count查询获得本次Hql查询所能获得的对象总数.
	 * 
	 * 本函数只能自动处理简单的hql语句,复杂的hql查询请另行编写count语句查询.
	 */
	private int countHqlResult(final String hql, final Object... values) {
		String countHql = prepareCountHql(hql);
		try {
			Long count = findUnique(countHql, values);
			return count.intValue();
		} catch (Exception e) {
			throw new RuntimeException("hql can't be auto count, hql is:" + countHql, e);
		}
	}
	
	/**
	 * 执行count查询获得本次Hql查询所能获得的对象总数.
	 * 
	 * 本函数只能自动处理简单的hql语句,复杂的hql查询请另行编写count语句查询.
	 */
	private int countHqlResult(final String hql, final Map<String, ?> values) {
		String countHql = prepareCountHql(hql);
		try {
			Long count = findUnique(countHql, values);
			return count.intValue();
		} catch (Exception e) {
			throw new RuntimeException("hql can't be auto count, hql is:" + countHql, e);
		}
	}
	
	/**
	 * select子句与order by子句会影响count查询,进行简单的排除.
	 * 
	 * @param orgHql
	 * @return
	 */
	private String prepareCountHql(String orgHql) {
		String upperCaseHql = orgHql.toUpperCase();
		int startIndex = upperCaseHql.indexOf("FROM ");
		int endIndex = upperCaseHql.indexOf("ORDER BY");
		if (endIndex > -1) {
			return "select count(*) from " + orgHql.substring(startIndex + 5, endIndex);
		} else {
			return "select count(*) from " + orgHql.substring(startIndex + 5);
		}
	}
	
	/**
	 * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数.
	 */
	private Object getFieldValue(final Object obj, final String fieldName) {
		Field field = getAccessibleField(obj, fieldName);

		if (field == null) {
			throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + obj + "]");
		}

		Object result = null;
		try {
			result = field.get(obj);
		} catch (IllegalAccessException e) {
			logger.error("impossible exception:{}", e.getMessage());
		}
		return result;
	}

	/**
	 * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数.
	 */
	private void setFieldValue(final Object obj, final String fieldName, final Object value) {
		Field field = getAccessibleField(obj, fieldName);

		if (field == null) {
			throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + obj + "]");
		}

		try {
			field.set(obj, value);
		} catch (IllegalAccessException e) {
			logger.error("impossible exception:{}", e.getMessage());
		}
	}

	/**
	 * 循环向上转型, 获取对象的DeclaredField,	 并强制设置为可访问.
	 * 
	 * 如向上转型到Object仍无法找到, 返回null.
	 */
	private Field getAccessibleField(final Object obj, final String fieldName) {
		for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
			try {
				Field field = superClass.getDeclaredField(fieldName);
				field.setAccessible(true);
				return field;
			} catch (NoSuchFieldException e) {//NOSONAR
				// Field不在当前类定义,继续向上转型
			}
		}
		return null;
	}
}